
//============================================================================//
// グローバル引数と定数

float4x4 g_mWorld;
float4x4 g_mWVP;
float4x4 g_mRefract;
float4x4 g_mReflect;
float3   g_vLight;
float3   g_vEye;
float4   g_cLight;
float4   g_cPLight;
float4   g_cAmbient;
float3   g_vPLPos[3];

texture g_tBump;
texture g_tDecal;
texture g_tRefract;
texture g_tReflect;

//============================================================================//
// サンプラー定義

sampler DecalSamp = sampler_state
{
	Texture = <g_tDecal>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
};

sampler BumpSamp = sampler_state
{
	Texture = <g_tBump>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
};

sampler RefractSamp = sampler_state
{
	Texture = <g_tRefract>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
};

sampler ReflectSamp = sampler_state
{
	Texture = <g_tReflect>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
};

sampler SkySamp = sampler_state
{
	Texture = <g_tDecal>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
	AddressV = Clamp;
};

//============================================================================//





//============================================================================//
// モデル描画
// vEyeとvLightはオブジェクト空間の位置に事前に変換しておく

struct RenderModelVS_Out
{
	float4 Pos : POSITION;
	float2 Tex : TEXCOORD0;
	float4 P   : TEXCOORD1;
	float3 L   : TEXCOORD2;
	float3 L0  : TEXCOORD3;
	float3 L1  : TEXCOORD4;
	float3 L2  : TEXCOORD5;
};

RenderModelVS_Out RenderModelVS(
	float4 Pos : POSITION,
	float3 Nor : NORMAL,
	float3 T   : TEXCOORD0,
	float3 B   : TEXCOORD1,
	float2 Tex : TEXCOORD2,
	uniform bool bSpecular
){
	RenderModelVS_Out Out;

	// 座標変換とテクスチャのコピー
	Out.Pos = mul( Pos, g_mWVP );
	Out.P   = mul( Pos, g_mWorld );
	Out.Tex = Tex;

	// 接空間への変換行列を求める
	float3x3 mTangent = transpose( float3x3(T,B,Nor) );

	if( bSpecular )
	{
		// スペキュラの場合はハーフベクトルを使う
		float3 E = normalize( g_vEye - Out.P );
		Out.L  = mul( g_vLight + E, mTangent );
		Out.L0 = mul( normalize(g_vPLPos[0]-Out.P) + E, mTangent );
		Out.L1 = mul( normalize(g_vPLPos[1]-Out.P) + E, mTangent );
		Out.L2 = mul( normalize(g_vPLPos[2]-Out.P) + E, mTangent );
	}
	else
	{
		// ランバード拡散
		Out.L  = mul( g_vLight, mTangent );
		Out.L0 = mul( normalize(g_vPLPos[0]-Out.P), mTangent );
		Out.L1 = mul( normalize(g_vPLPos[1]-Out.P), mTangent );
		Out.L2 = mul( normalize(g_vPLPos[2]-Out.P), mTangent );
	}

	return Out;
}

float4 CalcPointLight( float3 P, float3 N, float3 L0, float3 L1, float3 L2 )
{
	float3 Dif, d2, NL;

	Dif  = g_vPLPos[0] - P;
	d2.r = dot(Dif,Dif);
	NL.r = dot(N,normalize(L0));

	Dif  = g_vPLPos[1] - P;
	d2.g = dot(Dif,Dif);
	NL.g = dot(N,normalize(L1));

	Dif  = g_vPLPos[2] - P;
	d2.b = dot(Dif,Dif);
	NL.b = dot(N,normalize(L2));

	float4 Lp;
	Lp.rgb = NL / (0.000005f*d2);
	Lp.a   = Lp.r + Lp.g + Lp.b;
	return Lp;
}

float4 RenderModelPS( RenderModelVS_Out In, uniform bool bSpecular ) : COLOR
{
	// 平行光源ライティング
	float3 N = normalize( 2*tex2D(BumpSamp,In.Tex)-1 );
	float3 L = normalize( In.L );
	float NL = max(0,dot(N,L));

	// 点光源ライティング
	float3 P  = In.P.xyz / In.P.w;
	float4 Lp = CalcPointLight( P, N, In.L0, In.L1, In.L2 );

	// スペキュラ項の場合
	if( bSpecular ) {
		NL = 32*pow(NL,4);
		Lp = pow(Lp,2);
	}

	// テクスチャカラーをかける
	float4 T = tex2D(DecalSamp,In.Tex);
	return T * ( Lp*g_cPLight + NL*g_cLight + g_cAmbient );
}

technique RenderModel
{
	pass P0
	{
		VertexShader = compile vs_1_1 RenderModelVS(false);
		PixelShader  = compile ps_2_0 RenderModelPS(false);

		CullMode = Cw;
	}
}

technique RenderModel2
{
	pass P0
	{
		VertexShader = compile vs_1_1 RenderModelVS(true);
		PixelShader  = compile ps_2_0 RenderModelPS(true);

		CullMode = Cw;
	}
}

//============================================================================//
// フロア描画
// 法線変換の必要は無い(どうせ単位行列なので)

struct RenderFloorVS_Out
{
	float4 Pos : POSITION;
	float4 Col : COLOR;
	float2 Tex : TEXCOORD;
};

RenderFloorVS_Out RenderFloorVS(
	float4 Pos : POSITION,
	float3 Nor : NORMAL,
	float2 Tex : TEXCOORD2      // 注意
){
	RenderFloorVS_Out Out;

	Out.Pos     = mul( Pos, g_mWVP );
	Out.Col.rgb = g_cLight * max(0,dot(Nor,g_vLight)) + g_cAmbient;
	Out.Col.a   = 0.0f;
	Out.Tex     = Tex;

	return Out;
}

float4 RenderFloorPS( RenderFloorVS_Out In ) : COLOR
{
	return In.Col * tex2D(DecalSamp,In.Tex);
}

technique RenderFloor
{
	pass P0
	{
		VertexShader = compile vs_1_1 RenderFloorVS();
		PixelShader  = compile ps_2_0 RenderFloorPS();

		CullMode = Cw;
	}
}

//============================================================================//






//============================================================================//
// 水面描画

struct RenderWaterVS_Out
{
	float4 Pos : POSITION;
	float2 Tex : TEXCOORD0;
	float3 Eye : TEXCOORD1;
	float4 Refract : TEXCOORD2;
	float4 Reflect : TEXCOORD3;
	float4 P   : TEXCOORD4;
	float3 L0  : TEXCOORD5;
	float3 L1  : TEXCOORD6;
	float3 L2  : TEXCOORD7;
};

RenderWaterVS_Out RenderWaterVS(
	float4 Pos : POSITION,
	float2 Tex : TEXCOORD
){
	RenderWaterVS_Out Out;
	Out.Pos = mul( Pos, g_mWVP );
	Out.Tex = Tex;
	Out.Eye = g_vEye - Pos;  // World==Identity
	Out.Refract = mul( Pos, g_mRefract );
	Out.Reflect = mul( Pos, g_mReflect );
	Out.P   = Pos;
	Out.L0  = g_vPLPos[0] - Pos;
	Out.L1  = g_vPLPos[1] - Pos;
	Out.L2  = g_vPLPos[2] - Pos;
	return Out;
}

float4 RenderWaterPS( RenderWaterVS_Out In ) : COLOR
{
	float3 N = normalize( 2*tex2D(BumpSamp,In.Tex).rgb-1 );

	// スペキュラ項
	float3 E = normalize( In.Eye );
	float3 H = normalize( E+g_vLight );
	float NH = max(0,dot(N,H));
	float Spe = pow(NH,64)*10;

	// 点光源ライティング
	float3 P = In.P.xyz / In.P.w;
	float4 Lp = CalcPointLight( P, N, E+In.L0, E+In.L1, E+In.L2 );
	Lp = pow(Lp,4) * g_cPLight;

	// 反射屈折マップの参照座標値。倍率は適当
	// 数字をあげた方がバンプの効果が得られる。
	// なんか屈折あげたら床のバンプ効果が殆どないような気がする。
	float4 t1 = In.Refract;
	float4 t2 = In.Reflect;
	t1.xy += N.xy*3;
	t2.xy += N.xy*3;

	// フレネル項を近似。
	// スケール値とバイアス値は適当。DoubleSteel参照
	// 光沢＝スケール、鏡面反射＝バイアス。
	float r = pow(1-dot(N,E),4) * 0.3f + 0.1f;

	// 屈折マップと反射マップをブレンド
	return (1-r) * ( tex2Dproj( RefractSamp, t1 ) ) +
	          r  * ( tex2Dproj( ReflectSamp, t2 ) + Spe + Lp );
}

technique RenderWater
{
	pass P0
	{
		VertexShader = compile vs_1_1 RenderWaterVS();
		PixelShader  = compile ps_2_0 RenderWaterPS();

		CullMode = None;
	}
}

//============================================================================//










//============================================================================//
// 空描画

struct RenderSkyVS_Out
{
	float4 Pos : POSITION;
	float2 Tex : TEXCOORD;
};

RenderSkyVS_Out RenderSkyVS(
	float4 Pos : POSITION,
	float2 Tex : TEXCOORD
){
	RenderSkyVS_Out Out;
	Out.Pos = mul( Pos, g_mWVP );
	Out.Tex = Tex;
	return Out;
}

float4 RenderSkyPS( RenderSkyVS_Out In ) : COLOR
{
	float4 T;
	T.rgb = tex2D( SkySamp, In.Tex );
	T.a   = 0.0f;
	return T;
}

technique RenderSky
{
	pass P0
	{
		VertexShader = compile vs_1_1 RenderSkyVS();
		PixelShader  = compile ps_1_1 RenderSkyPS();

		ZFunc = Always;
	}
}

//============================================================================//
// フレア描画

struct RenderFlareVS_Out
{
	float4 Pos : POSITION;
	float2 Tex : TEXCOORD;
};

RenderFlareVS_Out RenderFlareVS(
	float4 Pos : POSITION,
	float2 Tex : TEXCOORD
){
	RenderFlareVS_Out Out;
	Out.Pos = mul( Pos, g_mWVP );
	Out.Tex = Tex;
	return Out;
}

float4 RenderFlarePS( RenderFlareVS_Out In ) : COLOR
{
	return g_cLight * tex2D( DecalSamp, In.Tex );
}

technique RenderFlare
{
	pass P0
	{
		VertexShader = compile vs_1_1 RenderFlareVS();
		PixelShader  = compile ps_1_1 RenderFlarePS();

		ZWriteEnable = False;
		AlphaBlendEnable = True;
		SrcBlend = SrcAlpha;
		DestBlend = InvSrcAlpha;
	}
}

//============================================================================//

